﻿using System;
using System.Collections.Generic;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage.Pickers;
using Windows.UI;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Shapes;
using System.IO;
// Pour en savoir plus sur le modèle d'élément Page de base, consultez la page http://go.microsoft.com/fwlink/?LinkId=234237

namespace SoundSynthLab
{
    /// <summary>
    /// Page de base qui inclut des caractéristiques communes à la plupart des applications.
    /// </summary>
    public sealed partial class InstrumentEditor : SoundSynthLab.Common.LayoutAwarePage
    {
        SoundSynth.SynthInstrument Instrument;
        SoundSynth.Oscillators.BaseOscillator SelectedOscillator;
        SoundSynth.SynthVoice Voice;
        public InstrumentEditor()
        {
            this.InitializeComponent();
            Voice = new SoundSynth.SynthVoice();
        }

        /// <summary>
        /// Remplit la page à l'aide du contenu passé lors de la navigation. Tout état enregistré est également
        /// fourni lorsqu'une page est recréée à partir d'une session antérieure.
        /// </summary>
        /// <param name="navigationParameter">Valeur de paramètre passée à
        /// <see cref="Frame.Navigate(Type, Object)"/> lors de la requête initiale de cette page.
        /// </param>
        /// <param name="pageState">Dictionnaire d'état conservé par cette page durant une session
        /// antérieure. Null lors de la première visite de la page.</param>
        protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
        {
        }

        /// <summary>
        /// Conserve l'état associé à cette page en cas de suspension de l'application ou de la
        /// suppression de la page du cache de navigation. Les valeurs doivent être conformes aux
        /// exigences en matière de sérialisation de <see cref="SuspensionManager.SessionState"/>.
        /// </summary>
        /// <param name="pageState">Dictionnaire vide à remplir à l'aide de l'état sérialisable.</param>
        protected override void SaveState(Dictionary<String, Object> pageState)
        {
        }

        private void pageRoot_Loaded(object sender, RoutedEventArgs e)
        {
            NewInstrument();
        }

        private void NewInstrument()
        {
            Instrument = new SoundSynth.SynthInstrument();
            ListOscillators.ItemsSource = Instrument.Oscillators;
        }

        private async void btnAddOscillator_Click(object sender, RoutedEventArgs e)
        {
            UICommandInvokedHandler handler = (h) =>
            {
                switch (h.Label)
                { 
                    case "Sine":
                        Instrument.Oscillators.Add(new SoundSynth.Oscillators.SineOscillator());
                        break;
                    case "Square":
                        Instrument.Oscillators.Add(new SoundSynth.Oscillators.SquareOscillator());
                        break;
                    case "Triangle":
                        Instrument.Oscillators.Add(new SoundSynth.Oscillators.TriangleOscillator());
                        break;
                    case "Sawtooth":
                        Instrument.Oscillators.Add(new SoundSynth.Oscillators.SawtoothOscillator());
                        break;
                }
                SelectedOscillator = Instrument.Oscillators.Last();
                ListOscillators.ItemsSource = null;
                ListOscillators.ItemsSource = Instrument.Oscillators;
                ListOscillators.InvalidateArrange();
                ListOscillators.SelectedItem = SelectedOscillator;

                UpdateAll();
            };
            var popupMenu = new PopupMenu();
            popupMenu.Commands.Add(new UICommand("Sine", handler));
            popupMenu.Commands.Add(new UICommand("Square", handler));
            popupMenu.Commands.Add(new UICommand("Triangle", handler));
            popupMenu.Commands.Add(new UICommand("Sawtooth", handler));
            var button = (Button)sender;
            var transform = button.TransformToVisual(this);
            var point = transform.TransformPoint(new Point(45, -10));
            await popupMenu.ShowAsync(point);
        }

        private void BtnDelete_Click(object sender, RoutedEventArgs e)
        {
            if (SelectedOscillator != null)
            {
                Instrument.Oscillators.Remove(SelectedOscillator);
                SelectedOscillator = Instrument.Oscillators.FirstOrDefault();
                ListOscillators.ItemsSource = null;
                ListOscillators.ItemsSource = Instrument.Oscillators;
                ListOscillators.SelectedItem = SelectedOscillator;
            }
            UpdateAll();
        }

        private void ListOscillators_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (ListOscillators.SelectedItem != null)
            {
                SelectedOscillator = (SoundSynth.Oscillators.BaseOscillator)ListOscillators.SelectedItem;
                GridOscillator.DataContext = ListOscillators.SelectedItem;
                GridOscillator.Visibility = Visibility.Visible;
                ChkInvert.IsChecked = SelectedOscillator.Invert;
            }
            else
            {
                SelectedOscillator = null;
                GridOscillator.DataContext = null;
                GridOscillator.Visibility = Visibility.Collapsed;
            }
        }

       

        private void UpdateAll()
        {
            Voice.LoadInstrument(Instrument);
            RedrawWave();
        }

        private void RedrawWave()
        {
            CanvasWave.Children.Clear();
            var Data = Instrument.GetData();
            var max = 2000;
            if (Data.Count > max)
            {
                List<float> Data2 = new List<float>();
                var r = Data.Count / max;
                for (int cpt = 0; cpt < Data.Count; cpt+=r)
                {
                    Data2.Add(Data[cpt]);
                }
                Data = Data2;
            }
            List<Point> Points = new List<Point>();
            for (int i = 0; i < Data.Count; i++)
            {
                var y = (-(Data[i] * CanvasWave.ActualHeight / 2.1))+
                    (CanvasWave.ActualHeight / 2);
                var x = ((double)i / (double)Data.Count) * CanvasWave.ActualWidth;
                Points.Add(new Point(x, y));
            }
            
            Line l = new Line()
            {
                X1 = 0,
                Y1 = CanvasWave.ActualHeight/2d,
                X2 = CanvasWave.ActualWidth,
                Y2 = CanvasWave.ActualHeight / 2d,
                StrokeThickness = 1,
                Stroke = new SolidColorBrush(Windows.UI.Colors.Black)
            };
            CanvasWave.Children.Add(l);
            DrawCurveFromPoints(Points);
        }

        private void DrawCurveFromPoints(List<Point>Points)
        {

            if (!Points.Any()) return;
            
            var pathFigure = new PathFigure();
            pathFigure.StartPoint = Points.First();
            // Foreach segment, we add a BezierSegment
            Point lastpoint = Points.First();

            for (int cpt = 1; cpt < Points.Count; cpt += 1)
            {
                pathFigure.Segments.Add(new BezierSegment()
                {

                    Point1 = Points[cpt - 1],
                    Point2 = new Point(
                        (Points[cpt - 1].X + Points[cpt].X) / 2,
                        (Points[cpt - 1].Y + Points[cpt].Y) / 2),
                    Point3 = Points[cpt]
                });
                lastpoint = Points[cpt];
            }

            if (lastpoint != Points.Last())
            {
                pathFigure.Segments.Add(new BezierSegment()
                {
                    Point1 = lastpoint,
                    Point2 = new Point((lastpoint.X + Points.Last().X) / 2, (lastpoint.Y + Points.Last().Y) / 2),
                    Point3 = Points.Last()
                });
            }
            var Geometry = new PathGeometry();
            Geometry.Figures.Add(pathFigure);
            Windows.UI.Xaml.Shapes.Path theLine = new Windows.UI.Xaml.Shapes.Path()
            {
                StrokeThickness=4,
                Stroke = new SolidColorBrush(Colors.Red),
                Data = Geometry
            };
            CanvasWave.Children.Add(theLine);
            
            
        }

        private void AnimatedKeyboard_NoteReleased()
        {
            Voice.StopNote();
        }

        private void AnimatedKeyboard_NotePressed(int note, int octave)
        {
            Voice.PlayNote(note + (octave * 12));
        }

        private void UpdatePreset(object sender, RangeBaseValueChangedEventArgs e)
        {
            UpdateAll();
        }

        private void UpdatePreset(object sender, RoutedEventArgs e)
        {
            if (SelectedOscillator != null) SelectedOscillator.Invert = ChkInvert.IsChecked.Value;
            UpdateAll();
        }

        private async void btnSave_Click(object sender, RoutedEventArgs e)
        {
            if (Instrument == null) return;
            

            var fileSavePicker = new FileSavePicker();
            fileSavePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
            fileSavePicker.FileTypeChoices.Add("Instrument file", new List<string> { ".txt" });
            fileSavePicker.DefaultFileExtension = ".txt";
            fileSavePicker.SettingsIdentifier = "picker1";

            var fileToSave = await fileSavePicker.PickSaveFileAsync();

            if (fileToSave != null)
            {
               // await fileToSave.DeleteAsync();
               // Windows.Storage.StorageFolder f = await Windows.Storage.StorageFolder.GetFolderFromPathAsync(fileToSave.Path.Substring(0, fileToSave.Path.LastIndexOf("\\")));
               // fileToSave = await f.CreateFileAsync(fileToSave.Path.Substring(fileToSave.Path.LastIndexOf("\\") + 1));
                var stream = await fileToSave.OpenStreamForWriteAsync();

                Instrument.SaveToFile(stream);
            }
        }

        private async void btnOpen_Click(object sender, RoutedEventArgs e)
        {
            FileOpenPicker picker = new FileOpenPicker();

            picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;

            picker.SettingsIdentifier = "picker1";
            picker.FileTypeFilter.Add(".txt");
            picker.CommitButtonText = "Select instrument file";
            var storageFile = await picker.PickSingleFileAsync();
            if (storageFile != null)
            {
                var stream = await storageFile.OpenStreamForReadAsync();
                Instrument = SoundSynth.SynthInstrument.ReadFromFile(stream);

                SelectedOscillator = Instrument.Oscillators.LastOrDefault();
                ListOscillators.ItemsSource = null;
                ListOscillators.ItemsSource = Instrument.Oscillators;
                ListOscillators.InvalidateArrange();
                ListOscillators.SelectedItem = SelectedOscillator;

                UpdateAll();
            }
        }
    }
}
